Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit dff0fd583120cf045384520147f4ec8708461a84


Parents : a2b0d1c
Author : Mark Qvist <mark@unsigned.io>
Date : 2025-11-23T23:57:39+01:00

Added filter support to LineSource

Changes

4 files changed, 27 insertions(+), 7 deletions(-)

M setup.py +4

Diff

diff --git a/LXST/Mixer.py b/LXST/Mixer.py
index d65133d..2a4cda5 100644
--- a/LXST/Mixer.py
+++ b/LXST/Mixer.py
@@ -112,6 +112,11 @@ class Mixer(LocalSource, LocalSink):
source_count += 1
if source_count > 0:
+ mixed_frame = np.clip(mixed_frame, -1.0, 1.0)
+ if RNS.loglevel >= RNS.LOG_DEBUG:
+ if mixed_frame.max() >= 1.0 or mixed_frame.min() <= -1.0:
+ RNS.log(f"Signal clipped on {self}", RNS.LOG_WARNING)
+
if self.codec: self.sink.handle_frame(self.codec.encode(mixed_frame), self)
else: self.sink.handle_frame(mixed_frame, self)
else:

diff --git a/LXST/Primitives/Telephony.py b/LXST/Primitives/Telephony.py
index 95b137d..997661c 100644
--- a/LXST/Primitives/Telephony.py
+++ b/LXST/Primitives/Telephony.py
@@ -11,6 +11,8 @@ from LXST.Sinks import LineSink
from LXST.Sources import LineSource, OpusFileSource
from LXST.Generators import ToneSource
from LXST.Network import SignallingReceiver, Packetizer, LinkSource
+from LXST.Filters import BandPass, AGC
+
PRIMITIVE_NAME = "telephony"
@@ -263,11 +265,12 @@ class Telephone(SignallingReceiver):
threading.Thread(target=job, daemon=True).start()
def __incoming_link_established(self, link):
- link.is_incoming = True
- link.is_outgoing = False
- link.ring_timeout = False
- link.answered = False
- link.profile = None
+ link.is_incoming = True
+ link.is_outgoing = False
+ link.ring_timeout = False
+ link.answered = False
+ link.is_terminating = False
+ link.profile = None
with self.call_handler_lock:
if self.active_call or self.busy:
RNS.log(f"Incoming call, but line is already active, signalling busy", RNS.LOG_DEBUG)
@@ -509,8 +512,9 @@ class Telephone(SignallingReceiver):
if self.active_call.is_incoming: self.signal(Signalling.STATUS_CONNECTING, self.active_call)
self.__prepare_dialling_pipelines()
+ filters = [BandPass(250, 8500), AGC()]
self.transmit_mixer = Mixer(target_frame_ms=self.target_frame_time_ms, gain=self.transmit_gain)
- self.audio_input = LineSource(preferred_device=self.microphone_device, target_frame_ms=self.target_frame_time_ms, codec=Raw(), sink=self.transmit_mixer)
+ self.audio_input = LineSource(preferred_device=self.microphone_device, target_frame_ms=self.target_frame_time_ms, codec=Raw(), sink=self.transmit_mixer, filters=filters)
self.transmit_pipeline = Pipeline(source=self.transmit_mixer,
codec=self.transmit_codec,
sink=Packetizer(self.active_call, failure_callback=self.__packetizer_failure))

diff --git a/LXST/Sources.py b/LXST/Sources.py
index 163e1f9..d10aaea 100644
--- a/LXST/Sources.py
+++ b/LXST/Sources.py
@@ -152,7 +152,7 @@ class LineSource(LocalSource):
MAX_FRAMES = 128
DEFAULT_FRAME_MS = 80
- def __init__(self, preferred_device=None, target_frame_ms=DEFAULT_FRAME_MS, codec=None, sink=None):
+ def __init__(self, preferred_device=None, target_frame_ms=DEFAULT_FRAME_MS, codec=None, sink=None, filters=None):
self.preferred_device = preferred_device
self.frame_deque = deque(maxlen=self.MAX_FRAMES)
self.target_frame_ms = target_frame_ms
@@ -165,6 +165,11 @@ class LineSource(LocalSource):
self._codec = None
self.codec = codec
self.sink = sink
+ self.filters = None
+
+ if filters != None:
+ if type(filters) == list: self.filters = filters
+ else: self.filters = [filters]
@property
def codec(self): return self._codec
@@ -218,6 +223,8 @@ class LineSource(LocalSource):
with self.backend.get_recorder(samples_per_frame=backend_samples_per_frame) as recorder:
while self.should_run:
frame_samples = recorder.record(numframes=self.samples_per_frame)
+ if self.filters != None:
+ for f in self.filters: frame_samples = f.handle_frame(frame_samples, self.samplerate)
if self.codec:
frame = self.codec.encode(frame_samples)
if self.sink and self.sink.can_receive(from_source=self):

diff --git a/setup.py b/setup.py
index f87ac21..ee93810 100644
--- a/setup.py
+++ b/setup.py
@@ -16,6 +16,10 @@ package_data = {
"Codecs/libs/pyogg/libs/win_amd64/*",
"Codecs/libs/pyogg/libs/macos/*",
"Sounds/*",
+ ],
+"LXST": [
+ "Filters.h",
+ "Filters.c",
]
}


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────